package json5

import (
	"bufio"
	"fmt"
	"io"
	"strings"
	"testing"
)

func Test_ParseJson(t *testing.T) {
	var val, exp, act string

	test := func(s string) string {
		r := strings.NewReader(s)
		if j, e := ParseJson(r); e == nil {
			return fmt.Sprint(j)
		} else {
			panic(e)
		}
	}

	val = ``
	exp = `<nil>`
	act = test(val)
	equal(t, exp, act, val)

	val = ` "value" `
	exp = `value`
	act = test(val)
	equal(t, exp, act, val)

	val = ` { "key" : "value" } `
	exp = `[{key value}]`
	act = test(val)
	equal(t, exp, act, val)

	val = ` [ "item" ] `
	exp = `[item]`
	act = test(val)
	equal(t, exp, act, val)

	// https://ru.wikipedia.org/wiki/JSON
	val = `{
  "firstName": "Иван",
  "lastName": "Иванов",
  "address": {
    "streetAddress": "Московское ш., 101, кв.101",
    "city": "Ленинград",
    "postalCode": 101101
  },
  "phoneNumbers": [
    "812 123-1234",
    "916 123-4567"
  ]
}
`
	exp = "[{firstName Иван} {lastName Иванов} {address [{streetAddress Московское ш., 101, кв.101} {city Ленинград} {postalCode 101101}]} {phoneNumbers [812 123-1234 916 123-4567]}]"
	act = test(val)
	equal(t, exp, act, val)

	// https://en.wikipedia.org/wiki/JSON
	val = `{
  "firstName": "John",
  "lastName": "Smith",
  "isAlive": true,
  "age": 27,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": "10021-3100"
  },
  "phoneNumbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    },
    {
      "type": "mobile",
      "number": "123 456-7890"
    }
  ],
  "children": [],
  "spouse": null
}
`
	exp = "[{firstName John} {lastName Smith} {isAlive true} {age 27} {address [{streetAddress 21 2nd Street} {city New York} {state NY} {postalCode 10021-3100}]} {phoneNumbers [[{type home} {number 212 555-1234}] [{type office} {number 646 555-4567}] [{type mobile} {number 123 456-7890}]]} {children []} {spouse null}]"
	act = test(val)
	equal(t, exp, act, val)

	val = `{
  book: {
    1: {
      title: Книга 1,
      annotation: Аннотация книги 1,
      publishing_id: 1,
      author: [ 1, 2 ],
      part: {
        1: {
          caption: Раздел 1,
          chapter: {
            1: {
              header: Глава 1.1,
              content: Содержимое главы 1.1
            },
            2: {
              header: Глава 1.2,
              content: Содержимое главы 1.2
            }
          }
        },
        2: {
          caption: Раздел 2,
          chapter: {
            1: {
              header: Глава 2.1,
              content: Содержимое главы 2.1
            },
            2: {
              header: Глава 2.2,
              content: Содержимое главы 2.2
            }
          }
        }
      }
    }
  },
  author: {
    1: {
      name: Автор 1
    },
    2: {
      name: Автор 2
    }
  },
  publishing: {
    1: {
      name: Издательство 1,
      address: Адрес издательства 1
    },
    2: {
      name: Издательство 2,
      address: Адрес издательства 2
    }
  }
}
`
	exp = "[{book [{1 [{title Книга 1} {annotation Аннотация книги 1} {publishing_id 1} {author [1 2]} {part [{1 [{caption Раздел 1} {chapter [{1 [{header Глава 1.1} {content Содержимое главы 1.1}]} {2 [{header Глава 1.2} {content Содержимое главы 1.2}]}]}]} {2 [{caption Раздел 2} {chapter [{1 [{header Глава 2.1} {content Содержимое главы 2.1}]} {2 [{header Глава 2.2} {content Содержимое главы 2.2}]}]}]}]}]}]} {author [{1 [{name Автор 1}]} {2 [{name Автор 2}]}]} {publishing [{1 [{name Издательство 1} {address Адрес издательства 1}]} {2 [{name Издательство 2} {address Адрес издательства 2}]}]}]"
	act = test(val)
	equal(t, exp, act, val)

	val = `
{ //document
  attr: "val",
  /* commentary */
  element: {
    attr1: " ABCDE  ",
  },
  element: {
    attr1: "'АБВГД' ",
    attr2: 123.45,
    text: "
      Char&quot;s: "aBcDe", 'АбВгД';
      Numbers: 12345.678.
    "
    },
  items: [ //item
    "Item_1",
    "Item_2",
  ],
}
	`
	exp = `[{//document
  attr val} {/* commentary */
  element [{attr1 ABCDE}]} {element [{attr1 'АБВГД'} {attr2 123.45} {text Char&quot;s: aBcDe, 'АбВгД';
      Numbers: 12345.678.}]} {items [//item
    Item_1 Item_2]}]`
	act = test(val)
	equal(t, exp, act, val)

}

func Test_parseElement(t *testing.T) {
	var val, exp, act string

	test := func(s string) string {
		bufr := bufio.NewReader(strings.NewReader(s))
		if a, _, e := parseElement(bufr, 0); e == nil || e == io.EOF {
			return fmt.Sprint(a)
		} else {
			panic(e)
		}
	}

	val = ``
	exp = `<nil>`
	act = test(val)
	equal(t, exp, act, val)

	val = `text`
	exp = `text`
	act = test(val)
	equal(t, exp, act, val)

	val = `{}`
	exp = `[]`
	act = test(val)
	equal(t, exp, act, val)

	val = `[]`
	exp = `[]`
	act = test(val)
	equal(t, exp, act, val)

	// array in object
	val = `{key:[value]}`
	exp = `[{key [value]}]`
	act = test(val)
	equal(t, exp, act, val)

	// object in array
	val = `[item,{key:value}]`
	exp = `[item [{key value}]]`
	act = test(val)
	equal(t, exp, act, val)

	// empty array in object
	val = `{key:[]} `
	exp = `[{key []}]`
	act = test(val)
	equal(t, exp, act, val)

	// empty array in array
	val = `[item,[]]`
	exp = `[item []]`
	act = test(val)
	equal(t, exp, act, val)

	// empty object in array
	val = `[item,{}]`
	exp = `[item []]`
	act = test(val)
	equal(t, exp, act, val)

	// empty object in object
	val = `{key:{}}`
	exp = `[{key []}]`
	act = test(val)
	equal(t, exp, act, val)

}

func Test_parseObject(t *testing.T) {
	var val, exp, act string

	test := func(s string) string {
		bufr := bufio.NewReader(strings.NewReader(s))
		if a, _, e := parseObject(bufr); e == nil || e == io.EOF {
			return fmt.Sprint(a)
		} else {
			panic(e)
		}
	}

	val = ``
	exp = `[]`
	act = test(val)
	equal(t, exp, act, val)

	val = `}`
	exp = `[]`
	act = test(val)
	equal(t, exp, act, val)

	val = `key:value}`
	exp = `[{key value}]`
	act = test(val)
	equal(t, exp, act, val)

	val = `key1:value1,key2:value2}`
	exp = `[{key1 value1} {key2 value2}]`
	act = test(val)
	equal(t, exp, act, val)

	val = `key1:value1,key2:value2,}`
	exp = `[{key1 value1} {key2 value2}]`
	act = test(val)
	equal(t, exp, act, val)

	val = `key1:value1,,key2:value2}`
	exp = `[{key1 value1} {key2 value2}]`
	act = test(val)
	equal(t, exp, act, val)

	val = `key:""}`
	exp = `[{key }]`
	act = test(val)
	equal(t, exp, act, val)

	val = `key:}`
	exp = `[{key <nil>}]`
	act = test(val)
	equal(t, exp, act, val)

	val = `key:,}`
	exp = `[{key <nil>}]`
	act = test(val)
	equal(t, exp, act, val)

	val = `key}`
	exp = `[{key <nil>}]`
	act = test(val)
	equal(t, exp, act, val)

	val = `key,}`
	exp = `[{key <nil>}]`
	act = test(val)
	equal(t, exp, act, val)

	val = `:}`
	exp = `[]`
	act = test(val)
	equal(t, exp, act, val)

	// object as value (quote)
	val = `object:"{key:value}"}`
	exp = `[{object {key:value}}]`
	act = test(val)
	equal(t, exp, act, val)

	// object as value (escape)
	val = `object:\{key:value\}}`
	exp = `[{object {key:value}}]`
	act = test(val)
	equal(t, exp, act, val)

}

func Test_parseArray(t *testing.T) {
	var val, exp, act string

	test := func(s string) string {
		bufr := bufio.NewReader(strings.NewReader(s))
		if a, _, e := parseArray(bufr); e == nil || e == io.EOF {
			return fmt.Sprint(a)
		} else {
			panic(e)
		}
	}

	val = ``
	exp = `[]`
	act = test(val)
	equal(t, exp, act, val)

	val = `]`
	exp = `[]`
	act = test(val)
	equal(t, exp, act, val)

	val = `item1]`
	exp = `[item1]`
	act = test(val)
	equal(t, exp, act, val)

	val = `item1,item2]`
	exp = `[item1 item2]`
	act = test(val)
	equal(t, exp, act, val)

	val = `item1,item2,]`
	exp = `[item1 item2]`
	act = test(val)
	equal(t, exp, act, val)

	val = `item1,,item2]`
	exp = `[item1 item2]`
	act = test(val)
	equal(t, exp, act, val)

	val = `item1,"",item2]`
	exp = `[item1  item2]`
	act = test(val)
	equal(t, exp, act, val)

	// array as item (quote)
	val = `item,"[array]"]`
	exp = `[item [array]]`
	act = test(val)
	equal(t, exp, act, val)

	// array as item (escape)
	val = `item,\[array\]]`
	exp = `[item [array]]`
	act = test(val)
	equal(t, exp, act, val)

}

func Test_parseString(t *testing.T) {
	var val, exp, act string

	test := func(s string) string {
		bufr := bufio.NewReader(strings.NewReader(s))
		if s, _, e := parseString(bufr, 0, ']', '}'); e == nil || e == io.EOF {
			return s
		} else {
			panic(e)
		}
	}

	val = ``
	exp = ``
	act = test(val)
	equal(t, exp, act, val)

	val = `""`
	exp = ``
	act = test(val)
	equal(t, exp, act, val)

	val = `text`
	exp = `text`
	act = test(val)
	equal(t, exp, act, val)

	val = `"text"`
	exp = `text`
	act = test(val)
	equal(t, exp, act, val)

	val = ` text `
	exp = `text`
	act = test(val)
	equal(t, exp, act, val)

	val = ` "text" `
	exp = `text`
	act = test(val)
	equal(t, exp, act, val)

	val = `"one \"two\" three"`
	exp = `one "two" three`
	act = test(val)
	equal(t, exp, act, val)

	val = `'one \'two\' three'`
	exp = `one 'two' three`
	act = test(val)
	equal(t, exp, act, val)

	val = `'one "two" three'`
	exp = `one "two" three`
	act = test(val)
	equal(t, exp, act, val)

	val = `"one 'two' three"`
	exp = `one 'two' three`
	act = test(val)
	equal(t, exp, act, val)

	val = `line1\r\n\tline1.1\r\nline2"`
	exp = "line1\r\n\tline1.1\r\nline2"
	act = test(val)
	equal(t, exp, act, val)

}
